其他
技术分享 | 如何写一个自己的 bcc 工具观测 MySQL?
作者:邓欢
爱可生 DMP 团队开发成员,主要负责 DMP 相关开发。
bcc 项目https://github.com/iovisor/bcc
生成 BPF 字节码
将 BPF 字节码加载到内核执行
通过 perf event 或者异步的方式从内核将数据拷贝到用户空间
#!/usr/bin/python
from bcc import BPF
bpf_text="""
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct data_t {
u32 pid;
u32 tgid;
u64 ts;
};
BPF_PERF_OUTPUT(events);
int do_apply_data_packet(struct pt_regs *ctx) {
struct task_struct *t = (struct task_struct *)bpf_get_current_task();
struct data_t data = {};
// thread id
data.pid = t->pid;
// thread group id(pid in user space)
data.tgid = t->tgid;
// bpf_ktime_get_ns returns u64 number of nanoseconds. Starts at system boot time but stops during suspend.
data.ts = bpf_ktime_get_ns();
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}
"""
头文件 <uapi/linux/ptrace.h>和<linux/sched.h>。
结构体 data_t 保存我们每次观测到的结果。
BPF_PERF_OUTPUT 定义了一个叫 events 的表,观测代码可以将观测数据写入到 events 表中,而 Python 代码可以从这个表中读取到观测数据。
do_apply_data_packet 函数收集观测数据。我们通过 bpf_get_current_task 获取了 MySQL 进程对应的结构体 task_struct,然后从获取了其中的 pid 和 tgid。这里需要注意的是 task_struct 中的 pid 其实对应的是线程 id,tgid 对应的是线程组 id(即用户空间中的进程 id)。时间 ts 是通过 bpf_ktime_get_ns 函数获取的,这个时间并不是一个时钟时间,而是系统启动的时间。最后通过 events 表的 perf_submit 方法将观测的数据提交到表中。
# get mysql function name and trace it.
path = "/root/sandboxes/mysql_base/8.0.18/lib/plugin/group_replication.so"
regex = "\\w+apply_data_packet\\w+"
symbols = BPF.get_user_functions_and_addresses(path, regex)
if len(symbols) == 0:
print("Can't find function 'apply_data_packet' in %s" % (path))
exit(1)
(mysql_func_name, addr) = symbols[0]
b = BPF(text=bpf_text)
b.attach_uprobe(name=path, sym=mysql_func_name, fn_name="do_apply_data_packet")
attach_kprobehttps://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#1-attach_kprobe
# output trace result.
print("Tracing MySQL server mgr apply_data_packet function")
print("%-14s %-6s %-6s" % ("SINCE_UP_TIME(s)", "PID", "THREAD"))
def print_event(cpu, data, size):
event = b["events"].event(data)
print("%-14s %-6s %-6s" % (event.ts/1000000000, event.tgid, event.pid))
b["events"].open_perf_buffer(print_event)
while 1:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
exit()
root@ubuntu:/tmp# python mgr_apply_data_packet.py
Tracing MySQL server mgr apply_data_packet function
SINCE_UP_TIME(s) PID THREAD
2165924 26387 27060
2165924 25810 27043
2165924 26962 27080
社区近期动态